package plp.orientadaObjetos2.memoria;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import plp.expressions2.expression.Id;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.memoria.ContextoCompilacaoOO1;
import plp.orientadaObjetos1.memoria.DefClasse;
import plp.orientadaObjetos1.memoria.colecao.ListaValor;
import plp.orientadaObjetos2.declaracao.funcao.DecFuncao;
import plp.orientadaObjetos2.util.MapPacoteArquivo;
import plp.orientadaObjetos2.util.SuperClasseMap;

public class ContextoCompilacaoOO2 extends ContextoCompilacaoOO1 implements AmbienteCompilacaoOO2{

	private Stack<Map<Id, DecFuncao>> pilhaFuncoes;

	private ArrayList<SuperClasseMap> arraySuperClasse;
	
	private ArrayList<MapPacoteArquivo> listArquivoAvaliados;
	
	public ContextoCompilacaoOO2(ListaValor entrada) {
		super(entrada);		
		arraySuperClasse = new ArrayList <SuperClasseMap> ();
		this.pilhaFuncoes = new Stack<Map<Id, DecFuncao>>();
	}
	
	/**
	 * Mapeia o id da sub-classe em uma super-classe.
	 */
	public void mapSuperClasse(Id classe, Id superClasse) throws ClasseNaoDeclaradaException {
		DefClasse defClasse = getDefClasse(superClasse);
		if (defClasse != null) {
			arraySuperClasse.add(new SuperClasseMap( classe, superClasse ));	
		}
	}

	/**
	 * Dado o id de uma classe, 
	 * recupera a definicao da super-classe. 
	 */
	public SuperClasseMap getSuperClasse(Id classe) throws ClasseNaoDeclaradaException {
		for(int i=0; i < arraySuperClasse.size(); i++){
			String nomeClasse = arraySuperClasse.get(i).getClasse().toString();
			
			if(nomeClasse.equalsIgnoreCase( classe.toString() )) {
				return arraySuperClasse.get(i);
			}
		}
		return null;
	}

	@Override
	public void incrementa() {
		super.incrementa();
		this.pilhaFuncoes.add(new HashMap<Id, DecFuncao>());
	}

	@Override
	public void mapFuncao(Id nomeFuncao, DecFuncao funcao) throws FuncaoJaDeclaradaException {
		Map<Id, DecFuncao> mapeamentoFuncoes = this.pilhaFuncoes.peek();
		if(mapeamentoFuncoes.put(nomeFuncao, funcao) != null){
			throw new FuncaoJaDeclaradaException(nomeFuncao);
		}
	}

	@Override
	public boolean arquivoJaAvaliado(MapPacoteArquivo mapeamento) {
		boolean jaAvaliado = false;
		
		if(this.listArquivoAvaliados != null){
			for(MapPacoteArquivo elemento : this.listArquivoAvaliados){
				if (elemento.getNomePacote().equals(mapeamento.getNomePacote())
						&& elemento.getNomeArquivo().equals(mapeamento.getNomeArquivo())) {
					return true;
				}
			}
		}
		return jaAvaliado;
	}

	@Override
	public void adicionarArquivo(MapPacoteArquivo mapeamento) {
		if(this.listArquivoAvaliados == null){
			this.listArquivoAvaliados = new ArrayList<MapPacoteArquivo>();
		}
		if(!arquivoJaAvaliado(mapeamento))
			this.listArquivoAvaliados.add(mapeamento);
	}
	
	public void imprime(){
		if(this.listArquivoAvaliados != null){
			for(MapPacoteArquivo elem : this.listArquivoAvaliados){
				System.out.println(String.format("%s - %s",elem.getNomePacote(), elem.getNomeArquivo()));
			}
		}else{
			System.out.println("Lista Vazia");
		}
	}

	@Override
	public List<MapPacoteArquivo> getListaArquivoAvaliado() {
		return this.listArquivoAvaliados;
	}
	
}
